////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   ParticleManager.h
//  Version:     v1.00
//  Created:     28/5/2001 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//	- 03:2006				 : Modified by Jan Mueller (Serialization)
//
////////////////////////////////////////////////////////////////////////////

#ifndef PART_MANAGER
#define PART_MANAGER

#include "ParticleEffect.h"
#include "BitFiddling.h"
#include "ParticleList.h"
#include "ParticleEnviron.h"
#include "ParticleUtils.h"
#include "ParticleMemory.h"
#include "IPerfHud.h"
#include "ParticleUtils.h"

class CParticleEmitter;
class CParticleContainer;
class CParticleWidget;

//////////////////////////////////////////////////////////////////////////
// Top class of particle system
class CParticleManager : public Cry3DEngineBase, public IParticleManager, public IVisAreaCallback
{
public:
  CParticleManager();
  ~CParticleManager();

	friend class CParticleWidget;

	// Provide access to extended CPartManager functions only to particle classes.
	ILINE static CParticleManager* Instance()
	{
		return static_cast<CParticleManager*>(m_pPartManager);
	}

	//////////////////////////////////////////////////////////////////////////
	// IParticleManager interface
	IParticleEffect* CreateEffect();
	void DeleteEffect( IParticleEffect* pEffect );
	IParticleEffect* FindEffect( cstr sEffectName, cstr sSource = "", bool bLoad = true );

	IParticleEffect* LoadEffect( cstr sEffectName, XmlNodeRef& effectNode, bool bLoadResources, const cstr sSource = NULL );
	bool LoadLibrary( cstr sParticlesLibrary, XmlNodeRef& libNode, bool bLoadResources );
	bool LoadLibrary( cstr sParticlesLibrary, cstr sParticlesLibraryFile = NULL, bool bLoadResources = false );
	void ClearCachedLibraries();

	IParticleEmitter* CreateEmitter( const PivotLocation& loc, const ParticleParams& Params, uint32 uEmitterFlags = 0, const SpawnParams* pSpawnParams = NULL );
	void DeleteEmitters( uint32 mask );
	IParticleEmitter* SerializeEmitter( TSerialize ser, IParticleEmitter* pEmitter = NULL );

	// Processing
	void Update();
	void RenderDebugInfo();
	void RenderSpuUsage();
	void OnStartRuntime();
	void OnFrameStart();
	void Reset( bool bIndependentOnly );
	void ClearRenderResources( bool bForceClear );
	void Serialize(TSerialize ser);
	void PostSerialize( bool bReading );
	void OnDeleteEmitter( IParticleEmitter *pEmitter );

	void SetTimer(ITimer* pTimer) { g_pParticleTimer = pTimer; };

	// Stats
	void GetMemoryUsage( ICrySizer* pSizer ) const;
	void GetCounts( SParticleCounts& counts );
	void ListEmitters( cstr sDesc = "", bool bForce = false );
	void ListEffects();
	void PrintParticleMemory();

	typedef VectorMap<const IParticleEffect*, SParticleCounts> TEffectStats;
	void CollectEffectStats( TEffectStats& mapEffectStats, float SParticleCounts::* pSortField ) const;

	//PerfHUD
	virtual void CreatePerfHUDWidget();

	// Summary:
	//	 Registers new particle events listener.
	virtual void AddEventListener(IParticleEffectListener *pListener);
	virtual void RemoveEventListener(IParticleEffectListener *pListener);

	//////////////////////////////////////////////////////////////////////////
	// Particle effects.
	//////////////////////////////////////////////////////////////////////////
	void RenameEffect( CParticleEffect* pEffect, cstr sNewName );
	XmlNodeRef ReadLibrary( cstr sParticlesLibrary );

	//////////////////////////////////////////////////////////////////////////
	// Emitters.
	//////////////////////////////////////////////////////////////////////////
	CParticleEmitter* CreateEmitter( const PivotLocation& loc, const IParticleEffect* pEffect, uint32 uEmitterFlags = 0, const SpawnParams* pSpawnParams = NULL );
	void UpdateEmitters( IParticleEffect *pEffect );

	//////////////////////////////////////////////////////////////////////////
	// Other methods
	IMaterial* GetLightShader() const
	{
		return m_pPartLightShader;
	}

	void CreateLightShader()
	{
		if (!m_pPartLightShader) 
		{
			m_pPartLightShader = MakeSystemMaterialFromShader("ParticlesNoMat");
		}
	}

	SPhysEnviron const& GetPhysEnviron() 
	{
		if (!(m_PhysEnv.m_nNonUniformFlags & EFF_LOADED))
			m_PhysEnv.GetWorldPhysAreas(~0, true);
		return m_PhysEnv;
	}

#ifdef bEVENT_TIMINGS
	struct SEventTiming
	{
		const CParticleEffect* pEffect;
		uint32 nContainerId;
		int nThread;
		cstr sEvent;
		float	timeStart, timeEnd;
	};
	int AddEventTiming( cstr sEvent, const CParticleContainer* pCont );

	inline int StartEventTiming( cstr sEvent, const CParticleContainer* pCont )
	{
		if (pCont && (GetCVars()->e_ParticlesDebug & AlphaBits('ed')))
		{
			WriteLock lock(m_EventLock);
			return AddEventTiming( sEvent, pCont ) * m_iEventSwitch;
		}
		else
			return 0;
	}
	inline void EndEventTiming( int iEvent )
	{
		WriteLock lock(m_EventLock);
		iEvent *= m_iEventSwitch;
		if (iEvent > 0)
			m_aEvents[iEvent].timeEnd = GetParticleTimer()->GetAsyncCurTime();
	}
#endif

	//
	// Constant engine data
	//
	ILINE uint32 GetAllowedEnvironmentFlags() const
	{
		return m_nAllowedEnvironmentFlags;
	}
	ILINE TrinaryFlags<uint32> const& GetRenderFlags() const
	{
		return m_RenderFlags; 
	}

	// Particle task hooks
	virtual void PrepareForRender()
		{}
	virtual void FinishRender();

	JobManager::SJobState* GetJobState()
	{
		return m_UpdateParticleStates.push_back();
	}

	// light profiler functions
	virtual void AddFrameTicks( uint64 nTicks ) { m_nFrameTicks += nTicks; }
	virtual void ResetFrameTicks() { m_nFrameTicks = 0; m_nFrameSyncTicks = 0; }
	virtual uint64 NumFrameTicks() const { return m_nFrameTicks;}	
	virtual uint32 NumEmitter() const { return m_nActiveEmitter;  }
	virtual void AddFrameSyncTicks( uint64 nTicks ) { m_nFrameSyncTicks += nTicks; }
	virtual uint64 NumFrameSyncTicks() const { return m_nFrameSyncTicks; }

	virtual void SyncComputeVerticesJobQueues();

private:

	struct CCompCStr
	{
		bool operator()( cstr a, cstr b ) const
		{
			return stricmp(a, b) < 0;
		}
	};

	//////////////////////////////////////////////////////////////////////////
	// Map of particle effect case-insensitive name to interface pointer.
	// The name key points to the name string in the actual effect,
	// so there is no string duplication.
	//typedef std::map< cstr, _smart_ptr<CParticleEffect>, CCompCStr > TEffectsList;
	// special key class, use md5 instead of a strcmp as an map key
	class SEffectsKey
	{
	public:
		SEffectsKey() { u64[0] = 0; u64[1] = 0; }
		SEffectsKey( const cstr &sName );		
		
		bool operator<( const SEffectsKey &rOther ) const 
		{ 
			return u64[0] == rOther.u64[0] ? u64[1] < rOther.u64[1] : u64[0] < rOther.u64[0];
		}

		void GetMemoryUsage( ICrySizer* pSizer ) const {}
	private:
		union
		{
			unsigned char c16[16];		
			uint64 u64[2];
		};
	};

	typedef VectorMap< SEffectsKey, _smart_ptr<CParticleEffect> > TEffectsList;
	typedef std::list<IParticleEffectListener*> TListenersList;
	TEffectsList								m_Effects;
	TListenersList							m_ListenersList;

	//////////////////////////////////////////////////////////////////////////
	// Loaded particle libs.
	std::map< string, XmlNodeRef, stl::less_stricmp<string> > m_LoadedLibs;

	//////////////////////////////////////////////////////////////////////////
	// Particle effects emitters, top-level only.
	//////////////////////////////////////////////////////////////////////////
	ParticlePoolList<CParticleEmitter>
															m_Emitters;

	bool												m_bRuntime;
	bool												m_bRegisteredListener;
	_smart_ptr<IMaterial>			  m_pPartLightShader;

	// Force features on/off depending on engine config.
	uint32											m_nAllowedEnvironmentFlags;	// Which particle features are allowed.
	TrinaryFlags<uint32>				m_RenderFlags;							// OS_ and FOB_ flags.

	SPhysEnviron								m_PhysEnv;									// Per-frame computed physics area information.

	// Particle job state pool.
	ParticlePoolList<JobManager::SJobState>	m_UpdateParticleStates;

	void Destroy(bool bIndependentOnly);
	void UpdateEngineData();

	// Listener for physics events.
	static int StaticOnPhysAreaChange(const EventPhys *pEvent)
	{
		CParticleManager::Instance()->OnPhysAreaChange(pEvent);
		return 0;
	}
	void OnPhysAreaChange(const EventPhys *pEvent)
	{
		// Require requerying world physics areas.
		m_PhysEnv.m_nNonUniformFlags &= ~EFF_LOADED;
	}

	// Listener for vis area events.
	virtual void OnVisAreaDeleted( IVisArea* pVisArea );

	CParticleEffect* FindLoadedEffect( cstr sEffectName )
	{
		TEffectsList::iterator it = m_Effects.find( sEffectName );
		if (it != m_Effects.end())
			return it->second;
		return NULL;
	}
	void EraseEmitter( CParticleEmitter *pEmitter );

	// Console command interface.
	static void CmdParticleListEffects( IConsoleCmdArgs* )
	{
		Instance()->ListEffects(); 
	}

	static void CmdParticleListEmitters( IConsoleCmdArgs* )
	{
		Instance()->ListEmitters("", true); 
	}
	static void CmdParticleMemory( IConsoleCmdArgs* )
	{ 
		Instance()->PrintParticleMemory(); 
	}

	bool LoadPreloadLibList(const cstr filename, const bool bLoadResources);

#ifdef bEVENT_TIMINGS
	DynArray<SEventTiming>		m_aEvents;
	int												m_iEventSwitch;
	CSpinLock									m_EventLock;
	float											m_timeThreshold;

	void LogEvents();
#endif

	CParticleWidget*					m_pWidget;

	// per frame lightwight timing informations
	uint64 m_nFrameTicks;
	uint64 m_nFrameSyncTicks;
	uint32 m_nActiveEmitter;
};

//PERFHUD
class CParticleWidget : public ICryPerfHUDWidget
{
public:
	CParticleWidget(minigui::IMiniCtrl* pParentMenu, ICryPerfHUD *pPerfHud, CParticleManager *pPartManager);
	~CParticleWidget();

	virtual void Reset() {}
	virtual void Update();
	virtual bool ShouldUpdate();
	virtual void LoadBudgets(XmlNodeRef perfXML) {}
	virtual void SaveStats(XmlNodeRef statsXML) {}
	virtual void Enable(int mode);
	virtual void Disable() { m_pTable->Hide(true); }

protected:

	minigui::IMiniTable* m_pTable;
	CParticleManager *m_pPartMgr;

	EPerfHUD_ParticleDisplayMode m_displayMode;
};

#ifdef bEVENT_TIMINGS

class CEventProfilerSection: public CFrameProfilerSection, public Cry3DEngineBase
{
public:
	CEventProfilerSection( CFrameProfiler *pProfiler, const CParticleContainer* pCont = 0 )
	: CFrameProfilerSection(pProfiler)
	{
		m_iEvent = m_pPartManager->StartEventTiming(pProfiler->m_name, pCont);
	}
	~CEventProfilerSection()
	{
		m_pPartManager->EndEventTiming(m_iEvent);
	}
protected:
	int m_iEvent;
};

	#define FUNCTION_PROFILER_CONTAINER(pCont) \
		static CFrameProfiler staticFrameProfiler( gEnv->pSystem, __FUNC__, PROFILE_PARTICLE ); \
		CEventProfilerSection eventProfilerSection( &staticFrameProfiler, pCont );

#else

	#define FUNCTION_PROFILER_CONTAINER(pCont)	FUNCTION_PROFILER_SYS(PARTICLE)

#endif



#endif // PART_MANAGER
	
